# ifndef __fstring_H_
# define __fstring_H_

// The class is like std::string but optimized to not occupy additional memory
// if the string is empty.
class fstring {
  public:
	// Constructors.
	fstring () : m_pData (NULL) {}
	fstring (const char * _str) {
		if (_str == NULL)
			m_pData = NULL;
		else
			_assign (_str, ::strlen (_str));
	}
	fstring (const char * _str, const char * _strEnd) {
		assert (_str <= _strEnd);
		_assign (_str, _strEnd - _str);
	}
	fstring (const char * _str, size_t _cLen) {
		_assign (_str, _cLen);
	}
	fstring (const fstring & _other) {
		_assign (_other.data (), _other.size ());
	}
	fstring (const std::string & _other) {
		_assign (_other.data (), _other.size ());
	}
	~fstring () {_release ();}

	void reserve (size_t) {}

	// Copy semantics.
	void assign (const fstring & _other) {
		_release ();
		_assign (_other.data (), _other.size ());
	}
	void assign (const char * _str) {
		_release ();
		if (_str != NULL)
			_assign (_str, ::strlen (_str));
	}
	void assign (const char * _str, const char * _strEnd) {
		assert (_str <= _strEnd);
		_release ();
		_assign (_str, _strEnd - _str);
	}
	void assign (const char * _str, size_t _cLen) {
		_release ();
		_assign (_str, _cLen);
	}
	void assign (size_t _cCount, char _ch) {
		_release ();
		if (_cCount == 1)
			_assign (& _ch, 1);
		else {
			char * const pData = new char [sizeof (size_t) + _cCount + 1];
			* reinterpret_cast<size_t *> (pData) = _cCount;
			::memchr (pData + sizeof (size_t), _ch, _cCount);
			(pData + sizeof (size_t)) [_cCount] = '\0';
			m_pData = pData + sizeof (size_t);
		}
	}

	void clear () {_release ();}

	fstring & operator = (const fstring & _other) {assign (_other); return * this;}
	fstring & operator = (const char * _str)      {assign (_str);   return * this;}
	fstring & operator = (char _ch)               {assign (1, _ch); return * this;}

	void append (const fstring & _other) {
		append (_other.c_str (), _other.size ());
	}
	void append (const char * _str)	{
		if (_str == NULL)
			return;
		append (_str, ::strlen (_str));
	}
	void append (const char * _str, const char * _strEnd) {
		assert (_str <= _strEnd);
		append (_str, _strEnd - _str);
	}
	void append (const char * _str, size_t _cLen);
	void append (size_t _cCount, char _ch);

	fstring & operator += (const fstring & _other) {append (_other); return * this;}
	fstring & operator += (const char * _str)      {append (_str);   return * this;}
	fstring & operator += (char _ch)               {append (1, _ch); return * this;}

	fstring operator + (const fstring & _other) const {
		fstring ret (* this);
		ret.append (_other);
		return ret;
	}
	fstring operator + (const char * _str) const {
		fstring ret (* this);
		ret.append (_str);
		return ret;
	}
	fstring operator + (char _ch) const {
		fstring ret (* this);
		ret.append (1, _ch);
		return ret;
	}

	void swap (fstring & _other) {std::swap (m_pData, _other.m_pData);}

	// Accessors.
	const char * data  () const {return m_pData ? m_pData : "";}
	const char * c_str () const {return data ();}
	size_t size () const {return m_pData ? * reinterpret_cast<const size_t *> (m_pData - sizeof (size_t)) : 0;}
	size_t length () const {return size ();}
	bool empty () const {return m_pData == NULL || * reinterpret_cast<const size_t *> (m_pData - sizeof (size_t)) == 0;}

	char   operator [] (size_t _cIndex) const {return m_pData [_cIndex];}
	char & operator [] (size_t _cIndex)       {return m_pData [_cIndex];}

	// Comparators.
	bool operator == (const fstring & _other) const;
	bool operator < (const fstring & _other) const;

	void LoadFromResource (UINT _uiResID);

	void ToLower();
	void ToUpper();

  private:
	char * m_pData;

	void _release () {
		if (m_pData)
			delete [] (m_pData - sizeof (size_t));
		m_pData = NULL;
	}
	void _assign (const char * _str, size_t _cLen);
};

# endif // __fstring_H_